﻿using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using AdTech.Common.WPF;

namespace AdTech.Snap
{
    #pragma warning disable 1591
    public class ConfigViewModel : GlassViewModel
    {
        // Fields
        private IConfiguration configModel;
        private readonly IDropboxFactory dropboxFactory;
        private readonly IConfiguration originalConfigModel;
        private bool hasPendingChanges;
        private DelegateCommand _SelectDefaultCommand;
        private DelegateCommand _SaveCommand;
        private DelegateCommand _ExtractDropboxUIDCommand;
        private DelegateCommand<RoutedEventArgs> _LoadedCommand;
        private DelegateCommand<CancelEventArgs> _ClosingCommand;

        // Properties
        #region DataModel Properties
        /// <summary>
        /// Gets or sets the color of the overlay when cropping screenshots
        /// </summary>
        public string OverlayColor
        {
            get { return this.configModel.OverlayColor; }
            set { if (!value.Equals(this.configModel.OverlayColor)) { this.configModel.OverlayColor = value; UpdatePendingChanges(true); NotifyPropertyChanged(() => this.OverlayColor); } }
        }
        /// <summary>
        /// Gets or sets the opacity of the overlay when cropping screenshots
        /// </summary>
        public double OverlayColorOpacity
        {
            get { return this.configModel.OverlayColorOpacity; }
            set { if (!value.Equals(this.configModel.OverlayColorOpacity)) { this.configModel.OverlayColorOpacity = value; UpdatePendingChanges(true); NotifyPropertyChanged(() => this.OverlayColorOpacity); } }
        }
        /// <summary>
        /// Gets or sets the default destination for screenshots.
        /// If set, the filenames will be generated uniquely.
        /// If set to null or empty a dialog will prompt the user to select a destination.
        /// </summary>
        [ValidatorAttribute(typeof(ValidPathRule))]
        public string DefaultFileDestination
        {
            get { return this.configModel.DefaultFileDestination; }
            set { if (!value.Equals(this.configModel.DefaultFileDestination)) { this.configModel.DefaultFileDestination = value; UpdatePendingChanges(true); ValidateAttribute("DefaultFileDestination", value); NotifyPropertyChanged(() => this.DefaultFileDestination); } }
        }
        /// <summary>
        /// Gets or sets a value whether to use dropbox integration
        /// This requires the DefaultFileDestination Property is set to the Dropbox public folder.
        /// If set to true, a public link will be copied to the clipboard afterwards
        /// </summary>
        public bool UseDropbox
        {
            get { return this.configModel.UseDropbox; }
            set { if (!value.Equals(this.configModel.UseDropbox)) { this.configModel.UseDropbox = value; if (!value) this.configModel.CreateQRCode = false; UpdatePendingChanges(true); NotifyPropertyChanged(() => this.UseDropbox); NotifyPropertyChanged(() => this.CreateQRCode); } }
        }
        /// <summary>
        /// Gets or sets a value whether a QRCode of the public Dropbox link should be created.
        /// This requires the Dropbox integration to be enabled.
        /// </summary>
        public bool CreateQRCode
        {
            get { return this.configModel.CreateQRCode; }
            set { if (!value.Equals(this.configModel.CreateQRCode)) { this.configModel.CreateQRCode = value; UpdatePendingChanges(true); NotifyPropertyChanged(() => this.CreateQRCode); } }
        }
        /// <summary>
        /// Gets or sets the Dropbox UserID which is required to generate a public link and copy it to the clipboard
        /// </summary>
        public int DropboxUID
        {
            get { return this.configModel.DropboxUID; }
            set { if (!value.Equals(this.configModel.DropboxUID)) { this.configModel.DropboxUID = value; UpdatePendingChanges(true); this.NotifyPropertyChanged(() => this.DropboxUID); } }
        }
        /// <summary>
        /// Gets or sets a value whether Snap is in AutoStart.
        /// </summary>
        public bool IsInSystemStart
        {
            get { return this.configModel.IsInSystemStart; }
            set { if (!value.Equals(this.configModel.IsInSystemStart)) { this.configModel.SetAutostart(value); this.NotifyPropertyChanged(() => this.IsInSystemStart); } }
        }
        #endregion
        #region Commands
        public ICommand SelectDestinationCommand
        {
            get
            {
                if (_SelectDefaultCommand == null)
                    _SelectDefaultCommand = new DelegateCommand(OnExecuteSelectDestination, OnCanExecuteSelectDestination);
                return _SelectDefaultCommand;
            }
        }
        public ICommand SaveCommand
        {
            get
            {
                if (_SaveCommand == null)
                    _SaveCommand = new DelegateCommand(OnExecuteSave, OnCanExecuteSave);
                return _SaveCommand;
            }
        }
        public ICommand ExtractDropboxUIDCommand
        {
            get
            {
                if (_ExtractDropboxUIDCommand == null)
                    _ExtractDropboxUIDCommand = new DelegateCommand(OnExecuteExtractDropboxUID, OnCanExecuteExtractDropboxUID);
                return _ExtractDropboxUIDCommand;
            }
        }
        public ICommand LoadedCommand
        {
            get
            {
                if (_LoadedCommand == null)
                    _LoadedCommand = new DelegateCommand<RoutedEventArgs>(OnExecuteLoaded);
                return _LoadedCommand;
            }
        }
        public ICommand ClosingCommand
        {
            get
            {
                if (_ClosingCommand == null)
                    _ClosingCommand = new DelegateCommand<CancelEventArgs>(OnExecuteClosing);
                return _ClosingCommand;
            }
        }
        #endregion

        // Constructors
        public ConfigViewModel(IConfiguration configModel, IDropboxFactory dropboxFactory)
        {
            this.configModel = configModel;
            this.dropboxFactory = dropboxFactory;
            this.originalConfigModel = configModel.Clone();
            UpdatePendingChanges(false);
            ValidateAllAttributes();
        }

        #region Commands
        protected virtual bool OnCanExecuteSelectDestination()
        {
            return true;
        }
        protected virtual void OnExecuteSelectDestination()
        {
            using (System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog())
            {
                dlg.SelectedPath = this.DefaultFileDestination;
                if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    this.DefaultFileDestination = dlg.SelectedPath;
            }
        }

        protected virtual bool OnCanExecuteSave()
        {
            return this.IsValid && (this.hasPendingChanges || this.configModel.IsNew);
        }
        protected virtual void OnExecuteSave()
        {
            this.configModel.Save();
            if (this.configModel.IsNew)
                ((DefaultConfiguration)this.configModel).IsNew = false;
            UpdatePendingChanges(false);
        }

        protected virtual bool OnCanExecuteExtractDropboxUID()
        {
            return true;
        }
        protected virtual void OnExecuteExtractDropboxUID()
        {
            if (!this.configModel.UseDropbox)
                return;
            DropboxUIDExtractorViewModel vm = new DropboxUIDExtractorViewModel(this.dropboxFactory);
            DropboxUIDExtractor wnd = new DropboxUIDExtractor { DataContext = vm };
            vm.RequestClose = () => wnd.Close();
            if (wnd.ShowDialog().Equals(true))
                this.DropboxUID = vm.DropboxUID;
        }

        [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
        protected virtual void OnExecuteLoaded(RoutedEventArgs e)
        {
            Window wnd = e.Source as Window;
            if (wnd == null)
                return;

            // Extend Aero frame
            IntPtr windowHandle = new WindowInteropHelper(wnd).Handle;
            Thickness margin = new Thickness(0);
            FrameworkElement extendedArea = wnd.FindName("border") as FrameworkElement;
            if (extendedArea != null)
                margin.Bottom += extendedArea.ActualHeight;
            this.ExtendWindowFrame(windowHandle, margin);
        }

        protected virtual void OnExecuteClosing(CancelEventArgs e)
        {
            if (this.hasPendingChanges)
            {
                if (!this.IsValid)
                {
                    MessageBox.Show("There are validation errors that have to be fixed before saving.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                    e.Cancel = true;
                    return;
                }

                MessageBoxResult result = MessageBox.Show(
                    "You have pending changes. Do you want to save these?", "Confirm pending changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Information);

                if (result == MessageBoxResult.Yes)
                    this.configModel.Save();

                if (result == MessageBoxResult.No)
                    this.configModel.MapFrom(originalConfigModel);

                if (result == MessageBoxResult.Cancel)
                    e.Cancel = true;
            }
            else
                this.configModel = originalConfigModel;
        }
        #endregion

        protected void UpdatePendingChanges(bool hasPendingChanges)
        {
            this.hasPendingChanges = hasPendingChanges;
        }
    }
    #pragma warning restore 1591
}
